using System;
using System.Collections.Generic;
using System.Text;

using gudusoft.gsqlparser;
using gudusoft.gsqlparser.Units;

namespace tableColumnRename
{
    class renameObject
    {
        TGSqlParser sqlparser;
        public string msg;
        public int renamedObjectsNum = 0;

        bool isModifyTableName = true;
        
        string sourceTable, targetTable, sourceColumn, targetColumn, sourceSchema, targetSchema;

        public string getModifiedText()
        {
            StringBuilder sb = new StringBuilder(1024);
            for (int i = 0; i < sqlparser.SourceTokenList.Count(); i++)
            {
                sb.Append(sqlparser.SourceTokenList[i].AsText);
            }

            return sb.ToString();
        }

        public renameObject(string sqltext)
        {
            sqlparser = new TGSqlParser(TDbVendor.DbVMssql);
            sqlparser.SqlText.Text = sqltext;
        }

        public int renameColumn(string sourceColumn, string targetColumn)
        {
            isModifyTableName = false;

            string[] names = sourceColumn.Split('.');
            if (names.Length == 2)
            {
                this.sourceSchema = null;
                this.sourceTable = names[0];
                this.sourceColumn = names[1];
            }
            else if (names.Length == 3)
            {
                this.sourceSchema = names[0];
                this.sourceTable = names[1];
                this.sourceColumn = names[2];
            }
            else
            {
                this.msg = "source column name must in syntax like this: schema.tablename.column or tablename.column";
                return -1;
            }

            this.targetColumn = targetColumn;
            names = targetColumn.Split('.');
            if (names.Length != 1) {
                this.msg = "target column name must in syntax like this: column";
                return -1;
            }

            int ret = sqlparser.Parse();
            if (ret != 0)
            {
                msg = "syntax error: " + sqlparser.ErrorMessages;
                return -1;
            }

            for (int i = 0; i < sqlparser.SqlStatements.Count(); i++)
            {
                TCustomSqlStatement sql = sqlparser.SqlStatements[i];
                searchTableTokensInStmt(sql, 0);

                for (int j = 0; j < sql.ChildNodes.Count(); j++)
                {
                    if (sql.ChildNodes[j] is TCustomSqlStatement)
                    {
                        tablesInStmt(sql.ChildNodes[j] as TCustomSqlStatement, 0);
                    }
                }

            }
            


            this.msg = "renamed column occurs:" + this.renamedObjectsNum;
            return renamedObjectsNum;
        }

        void searchTableTokensInStmt(TCustomSqlStatement stmt, int level)
        {

            TSourceTokenList tokenList = stmt.TableTokens;
           

            TSourceToken st = null;
            for (int i = 0; i < tokenList.Count(); i++)
            {
                bool isThisTable = true;

                st = tokenList[i];

                isThisTable = st.AsText.Equals(this.sourceTable, StringComparison.CurrentCultureIgnoreCase);
                if (!isThisTable) { continue; }

                if (this.sourceSchema != null)
                {
                    if (st.ParentToken != null)
                    {
                        isThisTable = st.ParentToken.AsText.Equals(this.sourceSchema, StringComparison.CurrentCultureIgnoreCase);
                    }
                    else
                    {
                        isThisTable = false;
                    }
                }

                if (!isThisTable) { continue; }

                modifyColumnsInTableToken(st, level + 2);
            }
        }


        public int renameTable(string sourceTable, string targetTable)
        {
            isModifyTableName = true;
            this.renamedObjectsNum = 0;
            string[] names = sourceTable.Split('.');
            if (names.Length == 1)
            {
                this.sourceTable = sourceTable;
                this.sourceSchema = null;
            }
            else if (names.Length == 2)
            {
                this.sourceSchema = names[0];
                this.sourceTable = names[1];
            }
            else
            {
                this.msg = "source table name must in syntax like this: schema.tablename, or tablename";
                return -1;
            }

            names = targetTable.Split('.');

            if (names.Length == 1)
            {
                this.targetTable = targetTable;
                this.targetSchema = null;
            }
            else if (names.Length == 2)
            {
                this.targetSchema = names[0];
                this.targetTable = names[1];
            }
            else
            {
                this.msg = "target table name must in syntax like this: schema.tablename, or tablename";
                return -1;
            }

            
            int ret = sqlparser.Parse();
            if (ret != 0)
            {
                msg = "syntax error: "+sqlparser.ErrorMessages;
                return -1;
            }

            for (int i = 0; i < sqlparser.SqlStatements.Count(); i++)
            {
                TCustomSqlStatement sql = sqlparser.SqlStatements[i];
                modifyTableTokensInStmt(sql, 0);

                for (int j = 0; j < sql.ChildNodes.Count(); j++)
                {
                    if (sql.ChildNodes[j] is TCustomSqlStatement)
                    {
                        tablesInStmt(sql.ChildNodes[j] as TCustomSqlStatement, 0);
                    }
                }

            }

           this.msg = "renamed table occurs:" + this.renamedObjectsNum;

            return renamedObjectsNum;

        }


        void tablesInStmt(TCustomSqlStatement stmt, int level)
        {

            if (isModifyTableName)
            {
                modifyTableTokensInStmt(stmt, level);
            }
            else
            {
                searchTableTokensInStmt(stmt, level);
            }

            for (int j = 0; j < stmt.ChildNodes.Count(); j++)
            {
                if (stmt.ChildNodes[j] is TCustomSqlStatement)
                {
                    tablesInStmt(stmt.ChildNodes[j] as TCustomSqlStatement, level + 1);
                }
            }

        }

        void modifyTableTokensInStmt(TCustomSqlStatement stmt, int level)
        {

            TSourceTokenList tokenList = stmt.TableTokens;

            TSourceToken st = null;
            bool isfound = false;

            for (int i = 0; i < tokenList.Count(); i++)
            {
                st = tokenList[i];
                isfound = false;
                if (st.AsText.Equals(this.sourceTable, StringComparison.CurrentCultureIgnoreCase))
                {
                    isfound = true;
                    if (this.sourceSchema != null)
                    { //check schema of this table
                        if (st.ParentToken != null)
                        {
                            isfound = this.sourceSchema.Equals(st.ParentToken.AsText, StringComparison.CurrentCultureIgnoreCase);
                        }
                        else
                        {
                            isfound = false;
                        }
                    }
                }

                if (isfound)
                { // rename this table
                    st.AsText = this.targetTable;
                    if (this.targetSchema != null)
                    {
                        if (st.ParentToken != null)
                        {
                            st.ParentToken.AsText = this.targetSchema;
                        }
                        else
                        {
                            st.AsText = this.targetSchema + "." + st.AsText;
                        }
                    }
                    this.renamedObjectsNum++;

                    TSourceToken rtt = null;
                    for (int j = 0; j < st.RelatedTokens.Count(); j++)
                    {
                        // reference table token
                        rtt = st.RelatedTokens[j];

                        if (rtt.DBObjType == TDBObjType.ttObjField)
                        {
                            // get all field tokens link with table token (those token not linked by syntax like tablename.fieldname)
                            // but like this : select f from t

                            continue;
                        }

                        rtt.AsText = this.targetTable;
                        if (this.targetSchema != null)
                        {
                            if (rtt.ParentToken != null)
                            {
                                rtt.ParentToken.AsText = this.targetSchema;
                            }
                            else
                            {
                                rtt.AsText = this.targetSchema + "." + rtt.AsText;
                            }
                        }
                        this.renamedObjectsNum++;
                    }
                }

                // modifyColumnsInTableToken(st, level + 2);
            }
        }

        void modifyColumnsInTableToken(TSourceToken st, int level)
        {
            if (st.RelatedToken != null)
            {
                // declared table alias token
                TSourceToken rt = st.RelatedToken;
                TSourceToken rrt = null;
                for (int i = 0; i < rt.RelatedTokens.Count(); i++)
                {
                    rrt = rt.RelatedTokens[i];
                    if (rrt.ChildToken.AsText.Equals(this.sourceColumn, StringComparison.CurrentCultureIgnoreCase))
                    {
                        rrt.ChildToken.AsText = this.targetColumn;
                        this.renamedObjectsNum++;
                    }
                    
                }
            }

            TSourceToken rtt = null;
            for (int i = 0; i < st.RelatedTokens.Count(); i++)
            {
                // reference table token
                rtt = st.RelatedTokens[i];

                if (rtt.DBObjType == TDBObjType.ttObjField)
                {
                    // get all field tokens link with table token (those token not linked by syntax like tablename.fieldname)
                    // but like this : select f from t

                    if (rtt.AsText.Equals(this.sourceColumn, StringComparison.CurrentCultureIgnoreCase))
                    {
                        rtt.AsText = this.targetColumn;
                        this.renamedObjectsNum++;
                    }
                }

                if (rtt.ChildToken != null)
                {
                    if (rtt.ChildToken.AsText.Equals(this.sourceColumn, StringComparison.CurrentCultureIgnoreCase))
                    {
                        rtt.ChildToken.AsText = this.targetColumn;
                        this.renamedObjectsNum++;
                    }
                    
                }
            }
        }



    }
}
